/* * Sonar W3C Markup Validation Plugin * Copyright (C) 2010 Matthijs Galesloot * dev@sonar.codehaus.org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sonar.plugins.web.markup.validation; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.List; import org.apache.commons.configuration.Configuration; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; import org.sonar.api.resources.InputFile; import org.sonar.api.utils.SonarException; import org.sonar.plugins.web.api.ProjectFileManager; import org.sonar.plugins.web.markup.constants.MarkupValidatorConstants; /** * Remote Validator using the W3C Markup Validation Service. * * @see http://validator.w3.org/docs/api.html * * @author Matthijs Galesloot * @since 1.0 * */ public final class MarkupValidator extends RemoteValidationService { private static final Logger LOG = Logger.getLogger(MarkupValidator.class); private static final String OUTPUT = "output"; // suffix for report with soap response private static final String REPORT_SUFFIX = ".smv"; private static final String SOAP12 = "soap12"; private static final String TEXT_HTML_CONTENT_TYPE = "text/html"; private static final String UPLOADED_FILE = "uploaded_file"; private final File buildDir; private final String validationUrl; public MarkupValidator(Configuration configuration, File buildDir) { super(configuration); this.buildDir = buildDir; this.validationUrl = configuration.getString(MarkupValidatorConstants.VALIDATION_URL, MarkupValidatorConstants.DEFAULT_URL); if (getWaitBetweenRequests() < 1000L && this.validationUrl.contains(MarkupValidatorConstants.DEFAULT_URL)) { LOG.warn("Minimum waiting time between requests is 1000 milliseconds"); setWaitBetweenRequests(1000L); } } /** * Post contents of HTML file to the W3C validation service. In return, receive a Soap response message. * * @see http://validator.w3.org/docs/api.html */ private void postHtmlContents(InputFile inputfile) { HttpPost post = new HttpPost(validationUrl); HttpResponse response = null; post.addHeader("User-Agent", "sonar-w3c-markup-validation-plugin/1.0"); try { LOG.info("W3C Validate: " + inputfile.getRelativePath()); // file upload MultipartEntity multiPartRequestEntity = new MultipartEntity(); String charset = CharsetDetector.detect(inputfile.getFile()); FileBody fileBody = new FileBody(inputfile.getFile(), TEXT_HTML_CONTENT_TYPE, charset); multiPartRequestEntity.addPart(UPLOADED_FILE, fileBody); // set output format multiPartRequestEntity.addPart(OUTPUT, new StringBody(SOAP12)); post.setEntity(multiPartRequestEntity); response = executePostMethod(post); // write response to report file if (response != null) { writeResponse(response, inputfile); } } catch (UnsupportedEncodingException e) { LOG.error(e); } finally { // release any connection resources used by the method if (response != null) { try { EntityUtils.consume(response.getEntity()); } catch (IOException ioe) { LOG.debug(ioe); } } } } /** * Create the path to the report file. */ public File reportFile(InputFile inputfile) { return new File(buildDir.getPath() + "/" + ProjectFileManager.getRelativePath(inputfile.getFile(), inputfile.getFileBaseDir()) + REPORT_SUFFIX); } /** * Validate a file with the W3C Markup Validation Service. */ public void validateFile(InputFile inputfile) { postHtmlContents(inputfile); } /** * Validate a list of files with the W3C Markup Validation Service */ public void validateFiles(List<InputFile> inputfiles) { int n = 0; for (InputFile inputfile : inputfiles) { // skip analysis if the report already exists File reportFile = reportFile(inputfile); if ( !reportFile.exists()) { if (n++ > 0) { waitBetweenValidationRequests(); } validateFile(inputfile); } } } private void writeResponse(HttpResponse response, InputFile inputfile) { if (response.getStatusLine().getStatusCode() == 200) { LOG.info("Validated:" + inputfile.getRelativePath()); File reportFile = reportFile(inputfile); reportFile.getParentFile().mkdirs(); Writer writer = null; try { writer = new FileWriter(reportFile); IOUtils.copy(response.getEntity().getContent(), writer); } catch (IOException e) { throw new SonarException(e); } finally { IOUtils.closeQuietly(writer); } } else { LOG.error("Response " + response.getStatusLine().getStatusCode() + ": " + response.getStatusLine().getReasonPhrase()); LOG.error("Failed to validate file: " + inputfile.getRelativePath()); } } }